﻿#pragma once

#include <stdio.h>
#include <windows.h>

#include <algorithm>
#include <array>
#include <ctime>
#include <fstream>
#include <ilcplex/ilocplex.h>
#include <iomanip>
#include <iostream>
#include <map>
#include <random>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <vector>

#pragma warning(disable : 4267 4244)

using namespace std;

class ICPPairingConfig;
class BasResLimit;
class BasPattern;

#define EPSILON 0.001
#define DAY_SECONED_DEFINE (24 * 60 * 60)
#define HOUR_SECONED_DEFINE (60 * 60)
#define DAY_OFFSET_SECONED_DEFINE (0 * 3600)

enum P_OR_C { NIL, P, C };
enum D_OR_I { D, R, T, I };
enum DUTYTYPE { DUTY_REGULAR, DUTY_REST };
enum LEGTYPE {
    LEG_REGULAR,
    LEG_BACKUP,
    LEG_VB,
    LEG_VA
}; // 正常可排人航班，备份航班，坐车置位，只能作为加机组（已经排人）

enum SP_LEVEL { SP_LEVEL_NIL, SP_LEVEL_1, SP_LEVEL_2 }; // 特殊机场等级,一类为1，二类为2，非特殊机场为空
enum TEL_AREA {
    TEL_AREA_NIL,
    TEL_AREA_T,
    TEL_AREA_J,
    TEL_AREA_U,
    TEL_AREA_Y
}; // 报务区域, 港澳台为T，日韩为J，东南亚为U，远东为Y，无为空
enum REST_LEVEL {
    REST_LEVEL_1,
    REST_LEVEL_2,
    REST_LEVEL_3,
    REST_LEVEL_NIL
};                            // 休息区等级,1、2、3分别表示一、二、三级休息区，无休息区为空
enum N2_E3_E4 { N2, E3, E4 }; // 扩编非扩编：N2-非扩编、E3-扩编3人制、E4-扩编4人制
enum DOMINATE_MODE { SINGLE, MULTI };
enum RULE_OR_COST { AS_COST, AS_RULE };

struct CCAR5 {
    int pri;
    int crewId;
    int crewNum;
    string flightCom;
    string flightCode;
    string acTypes;
    string acRegs;
    string airports;
    time_t minFdp;
    time_t maxFdp;
};
inline bool comCCAR5ByPri(const CCAR5& ccar1, const CCAR5& ccar2) { return ccar1.pri < ccar2.pri; }

struct FlightInfo {
    string flightNo;
    string depCode3;
    string arrCode3;
};

struct DepArr {
    string depCode3;
    string arrCode3;
};

struct AirportConfig {
    string acTypeCrew;
    string dep;
    string arr;
    time_t signIn;
    time_t signout;
    time_t drivingIn;
    time_t drivingOut;
};

struct baseDirect {
    string acTypes;
    string base;
    string include_airport_list;
    string exclude_airport_list;
    double fen;
};

struct ruleDP {
    string bases;
    string ac_type_crews;
    int legs_lb;
    int legs_ub;
    string two_round_trip_yn;
    string same_aircraft_yn;
    int dp_ub; // 日执勤期限制（分钟）
};

struct PairingInfo {
    time_t flightDate;
    string acType;
    string flightCom;
    string depCode3;
    string arrCode3;
    time_t td;
    time_t ta;
    int crewId;
    string base;
};

struct SchConfigB {
  private:
    string _base;
    string _acTypes;
    int _crewId;

  public:
    SchConfigB(string base, string acTypes, int crewId) : _base(base), _acTypes(acTypes), _crewId(crewId) {}
    ~SchConfigB(){};
    string getBase() { return _base; }
    string getAcTypes() { return _acTypes; }
    int getCrewId() const { return _crewId; }
};

struct personSchedule {
    string pcode;
    time_t fromDate;
    time_t toDate;
    string flightType;
    string flightCom;
};

struct personTechInfo {
    string acTypeCrew;
    string techNo;
};

struct BaseDailyCrewCnt {
    string _base;
    time_t _flightDate;
    int _crewCnt;

  public:
    BaseDailyCrewCnt(string base, time_t flightDate, int crewCnt)
        : _base(base), _flightDate(flightDate), _crewCnt(crewCnt) {}
    ~BaseDailyCrewCnt(){};
    string getBase() { return _base; }
    time_t getFlightDate() { return _flightDate; }
    int getCrewCnt() const { return _crewCnt; }
};

class ICPUtility {
  public:
    static string INPUTPATH;
    static string OUTPUTPATH;
    static string moduleFlag;
    static string filiale;

    static time_t bgnDate;
    static time_t endDate;

    static time_t dummyBgnDate;
    static time_t dummyEndDate;

    static time_t bgnTime;
    static time_t endTime;

    /* param121 */
    static time_t signIn;              // 签入时间
    static time_t signOut;             // 签出时间
    static time_t drivingIn;           // 前车程时间
    static time_t drivingOut;          // 后车程时间
    static time_t breakInterval;       // 二次进场判断标准
    static time_t minTransferInterval; // 换机间隔判断标准
    static time_t minRestInterval;     // 隔日休息时间标准(10小时+裕度值)
    static time_t minRestTimeZone6; // 2个飞行执勤期的最小间隔（跨6时区，回基地后必须安排48小时休息）
    static time_t day5Buffer;           // 不得连续5个日历日执行任何飞行任务的限制裕度值
    static int maxBaseCnt;              // 一个任务环最多包含多少个基地
    static int maxDaysInPairing;        // 任务环最大天数
    static int maxFlyDayInPairing;      // 任务环航班任务最大天数
    static int maxDeadheadNumInPairing; // 单个任务环中最大置位次数
    static int maxLayoverAirportCnt;    // 单个任务环中最大过夜机场数次数
    static int maxLegNumInDuty;         // 单个Duty最大航段数
    static time_t maxFlyTime;           // 单个任务最大飞行时长(非两段往返基地)
    static time_t maxFlyTimeClosed;     // 单个任务最大飞行时长(两段往返基地)

    static time_t timeSecondCheckInShort; // 二次进退场休息时间小于1小时
    static time_t timeMaxRestNonBase;     // 在外过夜休息时间不超过 48 小时（软约束）

    static time_t timeLongDay;             // 早晚跳
    static pair<time_t, time_t> earlyDuty; // 早班时间区间depTime[6:00, 9:00]
    static pair<time_t, time_t> lateDuty1; // 晚班时间区间arrTime[19:00, 24:00],(00:00, 6:00]
    static pair<time_t, time_t> lateDuty2; // 晚班时间区间arrTime[19:00, 24:00],(00:00, 6:00]

    // 过站时间标准
    static int minTransferVbFlt; // 坐车与航班的最小间隔
    static int minTransferFltVb; // 航班与坐车的最小间隔
    static int minTransferVaFlt; // 搭机与航班的最小间隔
    static int minTransferFltVa; // 航班与搭机的最小间隔
    static int minTransferVaVa;  // 搭机与搭机的最小间隔

    /* cost */
    static double costCover;          // 1航班覆盖成本
    static double costDeadheadVA;     // 2座机
    static double costDeadheadVB;     // 3坐车
    static double costPerCrewPerDay;  // 4人天成本
    static double costDutyMornEven;   // 5早晚班
    static double costDutyMorning;    // 6早班
    static double costDutyEvening;    // 7晚班
    static double costLayoverDays;    // 8过夜天数
    static double costBreak;          // 9二次进场成本
    static double costBreakLeg4;      // 10单日飞4段且需要二次进场
    static double costBreakMulti;     // 11单日多个二次进场
    static double costBreakShort;     // 12二次进退场休息时间小于1小时成本
    static double costFreeOneDay;     // 13外站空休1天成本
    static double costFreeTwoDay;     // 14外站空休2天成本
    static double costLayoverAirport; // 15排班周期内的不同过夜站点的个数超过2（不含）成本
    static double costLayoverHome;    // 16过家门而不入
    static double costLayoverNonBase; // 17非基地过夜
    static double costThinTask;       // 18瘦班成本
    static double costDutyTransfer;   // 19同日换飞机
    static double costVoyageOrderly;  // 20任务环稳定性成本
    static double costExcessCrew;     // 24超额成本
    static double costUnclosed;       // 25未闭环扣分
    static double costDP;             // 26占场时间越短越好

    /*profit*/
    static double profitHisRatio;     // 21历史的拟合度收益
    static double profitMatchPattern; // 22模板匹配收益
    static double profitRoundTrip;    // 23经停航班或来回程尽量组在一起

    /* alg parameter */
    static bool isExpModel; // 是否输出模型
    static double gap;
    static double fixUbBar;
    static double fixLbBar;
    static int minBetterCols;
    static int slowImproveIter;
    static double dominateBar;
    static DOMINATE_MODE dominateMode;
    static RULE_OR_COST baseAsMode;

    /* customerize parameter */
    static string crmForeignAirport;
    static int ctmNumMinForeignPairingPerDay;

    // 基地资源约束
    static vector<ICPPairingConfig*> pairingConfigList;
    static vector<BasResLimit*> basResLimitList;
    static map<string, int> mapBaseDailyCrewCnt;
    static vector<BaseDailyCrewCnt*> baseDailyCrewCntList;

    static vector<string> basBaseStrList;               // 基地串
    static set<string> setRule;                         // 规则启用集合
    static vector<CCAR5> ccar5List;                     // CCAR5运行标准
    static map<string, AirportConfig> mapAirportConfig; // 机场进离场及前车程后车程时间
    static map<string, double> mapBaseOverstaff;        // 基地资源紧张程度
    static vector<baseDirect> lstBaseDirect;            // 基地倾向性执行航班
    static map<string, double> mapDutyRatio;            // 历史（默认过去两周）Duty上FlightCom的拟合度
    static map<string, double> mapLinkRatio;            // 历史（默认过去两周）Duty上航班衔接的拟合度
    static vector<string> lstDutyVoyageZhuZhan;         // 历史（默认过去两周）驻站Duty上FlightCom列表
    static map<string, double> mapPairingVoyageMultiDay; // 历史（默认过去两周）驻站Pairing上PairingVoyage列表
    static map<string, double> mapPairingRatio; // 历史（默认过去两周）Pairing上FlightCom的拟合度
    static vector<BasPattern*> lstPattern;
    static vector<SchConfigB*> lstSchConfigB;  // 备份机组配置标准
    static map<string, double> linkDayDualMap; // DUTY衔接的稳定性

    static bool isDebug;
};

inline bool isUTF8(string filePath) {
    ifstream fin(filePath, ios::binary);
    unsigned char s2;
    fin.read((char*)&s2, sizeof(s2)); // 读取第一个字节，然后左移8位
    int p = s2 << 8;
    fin.read((char*)&s2, sizeof(s2)); // 读取第二个字节
    p += s2;

    string code;

    switch (p) // 判断文本前两个字节
    {
    case 0xfffe: // 65534
        code = "Unicode";
        // cout << "unicode:" << filePath << endl;
        break;
    case 0xfeff: // 65279
        code = "Unicode big endian";
        // cout << "Unicode big endian:" << filePath << endl;
        break;
    case 0xefbb: // 61371
        code = "UTF-8";
        // cout << "UTF-8:" << filePath << endl;
        break;
    default:
        code = "ANSI";
        // cout << "ANSI:" << filePath << endl;
    }
    fin.close();

    if (code == "UTF-8") {
        return true;
    }
    return false;
};

inline string convertUtf8ToGbk(string src_str) {
    int len = MultiByteToWideChar(CP_UTF8, 0, src_str.c_str(), -1, NULL, 0);
    wchar_t* wszGBK = new wchar_t[len + 1];
    memset(wszGBK, 0, len * 2 + 2);
    MultiByteToWideChar(CP_UTF8, 0, src_str.c_str(), -1, wszGBK, len);
    len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
    char* szGBK = new char[len + 1];
    memset(szGBK, 0, len + 1);
    WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
    string strTemp(szGBK);
    if (wszGBK)
        delete[] wszGBK;
    if (szGBK)
        delete[] szGBK;
    return strTemp;
};

inline vector<string> splitString(const string& str, const string& pattern) {
    vector<string> resVec;

    if ("" == str) {
        return resVec;
    }
    // 方便截取最后一段数据
    string strs = str + pattern;

    size_t pos = strs.find(pattern);
    size_t size = strs.size();

    while (pos != string::npos) {
        string x = strs.substr(0, pos);
        resVec.push_back(x);
        strs = strs.substr(pos + 1, size);
        pos = strs.find(pattern);
    }

    return resVec;
};

inline time_t makeTime(int yr, int mon, int day, int hr = 0, int min = 0, int sec = 0) {
    struct tm timeinfo;
    timeinfo.tm_year = yr - 1900;
    timeinfo.tm_mon = mon - 1;
    timeinfo.tm_mday = day;
    timeinfo.tm_hour = hr;
    timeinfo.tm_min = min;
    timeinfo.tm_sec = sec;

    return mktime(&timeinfo);
}

inline string getTimeStr(time_t t) {
    struct tm timeinfo;
    char buffer[128];
    localtime_s(&timeinfo, &t);
    strftime(buffer, 128, "%Y-%m-%d %H:%M:%S", &timeinfo);

    return buffer;
}

inline string getDateStr(time_t t) {
    struct tm timeinfo;
    char buffer[128];
    localtime_s(&timeinfo, &t);
    strftime(buffer, 128, "%Y/%m/%d", &timeinfo);

    return buffer;
}

inline vector<string> mixTwoVector(vector<string> v1, vector<string> v2) {
    if (v2.size() == 0)
        return v1;
    if (v1.size() == 0)
        return v2;
    if (v1.size() == 0 && v2.size() == 0)
        return ICPUtility::basBaseStrList;
    vector<string> result;
    for (int i = 0; i < v1.size(); i++) {
        if (find(v2.begin(), v2.end(), v1[i]) != v2.end()) {
            result.push_back(v1[i]);
        }
    }
    return result;
}

inline time_t convertToDateTime(string str) {
    char* token;
    char* tmp;

    token = strtok_s((char*)str.c_str(), "/-: ,", &tmp);
    int yr = atoi(token);

    token = strtok_s(NULL, "/-: ,", &tmp);
    int mon = atoi(token);

    token = strtok_s(NULL, "/-: ,", &tmp);
    int day = atoi(token);

    token = strtok_s(NULL, "/-: ,", &tmp);
    int hr = atoi(token);

    token = strtok_s(NULL, "/-: ,", &tmp);
    int min = atoi(token);

    int sec = 0;
    token = strtok_s(NULL, "/-: ,", &tmp);
    if (token != NULL) {
        sec = atoi(token);
    }

    return makeTime(yr, mon, day, hr, min, sec);
}

inline time_t convertToDate(time_t time) {
    struct tm timeinfo;
    localtime_s(&timeinfo, &time);
    int yr = timeinfo.tm_year + 1900;
    int mon = timeinfo.tm_mon + 1;
    int day = timeinfo.tm_mday;

    return makeTime(yr, mon, day);
}

inline time_t convertToDate(string str) {
    char* token;
    char* tmp;

    token = strtok_s((char*)str.c_str(), "/-: ,", &tmp);
    int yr = atoi(token);

    token = strtok_s(NULL, "/-: ,", &tmp);
    int mon = atoi(token);

    token = strtok_s(NULL, "/-: ,", &tmp);
    int day = atoi(token);

    struct tm timeinfo;
    timeinfo.tm_year = yr - 1900;
    timeinfo.tm_mon = mon - 1;
    timeinfo.tm_mday = day;
    timeinfo.tm_hour = 0;
    timeinfo.tm_min = 0;
    timeinfo.tm_sec = 0;

    return mktime(&timeinfo);
}

inline time_t convertToHrMi(time_t time) {
    struct tm timeinfo;
    localtime_s(&timeinfo, &time);
    int hr = timeinfo.tm_hour;
    int min = timeinfo.tm_min;
    int sec = timeinfo.tm_sec;
    return (time_t(hr * 3600 + min * 60 + sec));
}

inline time_t getMonthFirstDay(time_t time) {
    struct tm timeinfo;
    localtime_s(&timeinfo, &time);
    int yr = timeinfo.tm_year + 1900;
    int mon = timeinfo.tm_mon + 1;
    int day = 1;

    return makeTime(yr, mon, day);
}

inline int getDaysBetween(time_t bgnTime, time_t endTime) {
    return (convertToDate(endTime) - convertToDate(bgnTime)) / DAY_SECONED_DEFINE + 1;
}

inline int getYear(time_t time) {
    struct tm timeinfo;
    localtime_s(&timeinfo, &time);
    return timeinfo.tm_year + 1900;
}

inline int getMonth(time_t time) {
    struct tm timeinfo;
    localtime_s(&timeinfo, &time);
    return timeinfo.tm_mon + 1;
}

inline int getDay(time_t time) {
    struct tm timeinfo;
    localtime_s(&timeinfo, &time);
    return timeinfo.tm_mday;
}

inline int getHour(time_t time) {
    struct tm timeinfo;
    localtime_s(&timeinfo, &time);
    return timeinfo.tm_hour;
}

inline int getMinute(time_t time) {
    struct tm timeinfo;
    localtime_s(&timeinfo, &time);
    return timeinfo.tm_min;
}

/* days since [日一二三四五六] -> [0123456] */
inline int getDayOfWeek(time_t time) {
    struct tm timeinfo;
    localtime_s(&timeinfo, &time);
    return timeinfo.tm_wday;
}

inline string trim(string s) {
    size_t n = s.find_last_not_of(" \r\n\t");
    if (n != string::npos) {
        s.erase(n + 1, s.size() - n);
    }
    n = s.find_first_not_of(" \r\n\t");
    if (n != string::npos) {
        s.erase(0, n);
    }

    return s;
}

inline bool isTimeConflict(time_t pStartTime1, time_t pEndTime1, time_t pStartTime2, time_t pEndTime2) {
    if (pEndTime2 < pStartTime1 || pEndTime1 < pStartTime2) {
        //"没有冲突"
        return false;
    } else {
        //"发生冲突"
        return true;
    }
}

inline bool isUtf8(const std::string& str) {
    int len = MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), NULL, 0);
    if (len == 0)
        return false;
    wchar_t* wstr = new wchar_t[len];
    if (!MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), wstr, len)) {
        delete[] wstr;
        return false;
    }
    bool ret = true;
    if (WideCharToMultiByte(CP_UTF8, 0, wstr, len, NULL, 0, NULL, NULL) == 0 &&
        GetLastError() == ERROR_NO_UNICODE_TRANSLATION) {
        ret = false;
    }
    delete[] wstr;
    return ret;
}

inline string Utf8ToAnsi(const std::string& utf8_str) {
    // convert UTF-8 to Unicode
    const int len = MultiByteToWideChar(CP_UTF8, 0, utf8_str.c_str(), -1, NULL, 0);
    if (len == 0)
        return "";
    wchar_t* unicode_str = new wchar_t[len];
    MultiByteToWideChar(CP_UTF8, 0, utf8_str.c_str(), -1, unicode_str, len);

    // convert Unicode to ANSI
    const int ansi_len = WideCharToMultiByte(CP_ACP, 0, unicode_str, -1, NULL, 0, NULL, NULL);
    if (ansi_len == 0) {
        delete[] unicode_str;
        return "";
    }
    char* ansi_str = new char[ansi_len];
    WideCharToMultiByte(CP_ACP, 0, unicode_str, -1, ansi_str, ansi_len, NULL, NULL);

    const std::string result(ansi_str);
    delete[] unicode_str;
    delete[] ansi_str;

    return result;
}

inline bool isStringNumeric(const std::string& str) {
    for (char c : str) {
        if (!(c >= '0' && c <= '9')) {
            return false;
        }
    }
    return true;
}

inline bool isLargeAcType(string acTypeCrew) {
    return acTypeCrew == "B747" || acTypeCrew == "B757" || acTypeCrew == "B767" || acTypeCrew == "B777" ||
           acTypeCrew == "B787" || acTypeCrew == "B797" || acTypeCrew == "A330" || acTypeCrew == "A350";
}

inline string ConvertPatternToVoyage(string pattern) {
    if (pattern.empty())
        return "";

    auto ary = splitString(pattern, "|");
    auto flightNos = splitString(ary[0], "/");
    vector<string> airports;
    while (ary[1].find("(") != string::npos) {
        airports.push_back(ary[1].substr(ary[1].find("(") + 1, 3));
        ary[1] = ary[1].substr(ary[1].find("(") + 3);
    }
    if (flightNos.size() + 1 != airports.size()) {
        return "";
    }

    string voyage;
    for (size_t i = 0; i < flightNos.size(); i++) {
        if (voyage.empty()) {
            voyage = "A" + flightNos[i] + airports[i] + airports[i + 1];
        } else {
            voyage = voyage + "-" + "A" + flightNos[i] + airports[i] + airports[i + 1];
        }
    }

    return voyage;
}